package org.jeecg.modules.innerApi.zhiYunSdApi.util;

import cn.hutool.core.util.ReflectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.google.gson.Gson;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.URI;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Consts;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.modules.innerApi.common.responseDto.FileStreamRtn;
import org.jeecg.modules.innerApi.zhiYunApi.util.ZhiyunApiResponse;
import org.jeecg.modules.innerApi.zhiYunSdApi.config.ShudianApiConfig;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLConnection;
import java.util.*;

/**
 * @description:
 * @author: niugeng
 * @create: 2023-08-30 15:21
 **/
@Component
public class ZhiyunSdApiCallUtil {

    @Resource
    private ShudianApiConfig shudianApiConfig;

    @Resource
    private RedisUtil redisUtil;

    private final String tokenKey="XSD-TOKEN";//存放在redis缓存里面的token

    private final long tokenTime=3300;//存放在redis缓存里面的token

    public static Gson gson = new Gson();


    // 生成token
    public String token(String time) {
        String token=null;
        token =redisUtil.get(tokenKey)!=null?String.valueOf(redisUtil.get(tokenKey)):null;
        if(StringUtils.isEmpty(token)) {
            // 生成调用token的sign
            String tokenSign = getTokenSign(time, shudianApiConfig.getKey(), shudianApiConfig.getSecret());
            System.out.println("调用token的sign-->" + tokenSign);
            String url = shudianApiConfig.getOpenserverUrl() + shudianApiConfig.getTokenUrl();
            token = tokenGet(url, shudianApiConfig.getKey(), time, tokenSign);
            redisUtil.set(tokenKey,token,tokenTime);
        }
        return token;
    }

    // 生成开放平台请求头里所需的参数
    public Map<String, String> getHeader(String interfacePath) {
        String time = getTime();// 时间戳
        // 获取token
        String token = token(time);

        // 获取接口sign
        String jkUrl = shudianApiConfig.getInterfaceUrl() + interfacePath;
        System.out.println("接口url->生成接口sign使用");
        System.out.println(jkUrl);
        String urlSign = getUrlSign(time, token, jkUrl);
        System.out.println("调用接口的sign-->" + urlSign);

        // 发起请求
        String url = shudianApiConfig.getOpenserverUrl() + jkUrl;
        System.out.println("接口全路径");
        System.out.println(url);
        Map<String, String> headerMap = new HashMap<>();
        headerMap.put("token", token);
        headerMap.put("sign", urlSign);
        headerMap.put("timestamp", time);
        headerMap.put("url", url);
        return headerMap;
    }

    private void addParams(Object obj,PostMethod postMethod,String prefix){
        if (obj != null) {
            Field[] fields = ReflectUtil.getFields(obj.getClass());
            for (Field field : fields) {
                Object fieldValue = ReflectUtil.getFieldValue(obj, field);
                String name = field.getName();
                if(StringUtils.isNotBlank(prefix)){
                    name = prefix + "[" +name+"]";
                }
                if(fieldValue == null){
                }else if(String.valueOf(fieldValue).isEmpty()){
                    postMethod.addParameter(name,"");
                }else if(fieldValue instanceof List){
                    List listValue = (List) fieldValue;
                    for (int i = 0; i < listValue.size(); i++) {
                        Object o = listValue.get(i);
                        addParams(o,postMethod, name+"["+i+"]");
                    }
                }else if(fieldValue instanceof String || ClassUtils.isPrimitiveOrWrapper(fieldValue.getClass())){
                    postMethod.setParameter(name, String.valueOf(fieldValue));
                }else if(fieldValue instanceof Map) {
                    Map mapValue = (Map) fieldValue;
                    Iterator<Map.Entry<String, Object>> iterator = mapValue.entrySet().iterator();
                    while (iterator.hasNext()) {
                        Map.Entry<String, Object> item = iterator.next();
                        String key = item.getKey();
                        Object value = item.getValue();
                        postMethod.setParameter(key, String.valueOf(value));
                    }

                }else{
                    postMethod.setParameter(name, String.valueOf(fieldValue));
                }
            }
        }
    }

    public <T> T request(String interfacePath, Object obj,Class<T> returnType) {
        Map<String, String> headerMap = getHeader(interfacePath);
        String url = headerMap.get("url");
        PostMethod postMethod = new PostMethod(url);
        addParams(obj,postMethod,null);
        postMethod.addRequestHeader("token", headerMap.get("token"));
        postMethod.addRequestHeader("sign", headerMap.get("sign"));
        postMethod.addRequestHeader("timestamp", headerMap.get("timestamp"));
        String res = doPost(postMethod);
        if(res.contains("token失效")){//第一次调用缓存的token失效时，重新获取token并进行调用
            redisUtil.del(tokenKey);
            return this.request(interfacePath,obj,returnType);
        }else {
            ZhiyunApiResponse zhiyunApiResponse = JSON.parseObject(res, ZhiyunApiResponse.class);
            if (zhiyunApiResponse.getCode() != 200) {
                throw new JeecgBootException(zhiyunApiResponse.getMsg());
            }
            return zhiyunApiResponse.getData().toJavaObject(returnType);
        }
    }

    public <T>ShudianResponse  request(String interfacePath, Object obj, TypeReference<ShudianResponse<T>> returnType) {
        Map<String, String> headerMap = getHeader(interfacePath);
        String url = headerMap.get("url");
        PostMethod postMethod = new PostMethod(url);
        addParams(obj,postMethod,null);
        postMethod.addRequestHeader("token", headerMap.get("token"));
        postMethod.addRequestHeader("sign", headerMap.get("sign"));
        postMethod.addRequestHeader("timestamp", headerMap.get("timestamp"));
        String res = doPost(postMethod);
        if(res.contains("token失效")){//第一次调用缓存的token失效时，重新获取token并进行调用
            redisUtil.del(tokenKey);
            return this.request(interfacePath,obj,returnType);
        }else {
            final ShudianResponse<T> tShudianResponse = JSON.parseObject(res, returnType);
            if (!tShudianResponse.isSuccess()) {
                throw new JeecgBootException(tShudianResponse.getMsg());
            }

            return tShudianResponse;
        }
    }

    public ZhiyunSdApiResponse request(String interfacePath, Object obj, String dzswjzh) {
        Map<String, String> headerMap = getHeader(interfacePath);
        String url = headerMap.get("url");
        JSONObject username=new JSONObject();
        username.put("username",dzswjzh);
        String res=null;
        PostMethod postMethod = new PostMethod(url);
        //添加公共请求头参数
        postMethod.addRequestHeader("token", headerMap.get("token"));
        postMethod.addRequestHeader("sign", headerMap.get("sign"));
        postMethod.addRequestHeader("timestamp", headerMap.get("timestamp"));
        //添加公共请求参数
        addParams(username,postMethod,null);
        //添加请求参数
        addParams(obj,postMethod,null);
        res = doPost(postMethod);
        if(res.contains("token失效")){//第一次调用缓存的token失效时，重新获取token并进行调用
            redisUtil.del(tokenKey);
            return this.request(interfacePath,obj,dzswjzh);
        }else{
            ZhiyunSdApiResponse zhiyunSdApiResponse = JSON.parseObject(res, ZhiyunSdApiResponse.class);
            return zhiyunSdApiResponse;
        }
    }

    public String requestStr(String interfacePath, Object obj, String dzswjzh) {
        Map<String, String> headerMap = getHeader(interfacePath);
        String url = headerMap.get("url");
        JSONObject username=new JSONObject();
        username.put("username",dzswjzh);
        PostMethod postMethod = new PostMethod(url);
        //添加公共请求头参数
        postMethod.addRequestHeader("token", headerMap.get("token"));
        postMethod.addRequestHeader("sign", headerMap.get("sign"));
        postMethod.addRequestHeader("timestamp", headerMap.get("timestamp"));
        //添加公共请求参数
        addParams(username,postMethod,null);
        //添加请求参数
        addParams(obj,postMethod,null);
        String res = doPost(postMethod);
        if(res.contains("token失效")){//第一次调用缓存的token失效时，重新获取token并进行调用
            redisUtil.del(tokenKey);
            return this.requestStr(interfacePath,obj,dzswjzh);
        }else{
            return res;
        }
    }

    public FileStreamRtn requestDownFlie(String interfacePath, Object obj) {
        Map<String, String> headerMap = getHeader(interfacePath);
        String url = headerMap.get("url");
        PostMethod postMethod = new PostMethod(url);
        addParams(obj,postMethod,null);
        postMethod.addRequestHeader("token", headerMap.get("token"));
        postMethod.addRequestHeader("sign", headerMap.get("sign"));
        postMethod.addRequestHeader("timestamp", headerMap.get("timestamp"));
        return doPostDownFile(postMethod);
    }

    /**
     * get请求
     * @param interfacePath
     * @param paramList
     * @param dzswjzh
     * @return
     */
    public ZhiyunSdApiResponse getRequest(String interfacePath,List<BasicNameValuePair> paramList , String dzswjzh) {
        Map<String, String> headerMap = getHeader(interfacePath);
        String url = headerMap.get("url");
        JSONObject username=new JSONObject();
        username.put("username",dzswjzh);
        String res=null;
        try {
            GetMethod getMethod=new GetMethod();
            //添加公共请求头参数
            getMethod.addRequestHeader("token", headerMap.get("token"));
            getMethod.addRequestHeader("sign", headerMap.get("sign"));
            getMethod.addRequestHeader("timestamp", headerMap.get("timestamp"));
            //添加公共请求参数
            String params="username="+dzswjzh;
            //添加请求参数
            params +="&"+EntityUtils.toString(new UrlEncodedFormEntity(paramList, Consts.UTF_8));
            getMethod.setURI(new URI(url + "?" + params, true, "UTF-8"));
            res = doGet(getMethod);
        }catch (Exception e){

        }
        if(res.contains("token失效")){//第一次调用缓存的token失效时，重新获取token并进行调用
            redisUtil.del(tokenKey);
            return this.getRequest(interfacePath,paramList,dzswjzh);
        }else{
            ZhiyunSdApiResponse zhiyunSdApiResponse = JSON.parseObject(res, ZhiyunSdApiResponse.class);
            return zhiyunSdApiResponse;
        }
    }

    // post请求返回文件流
    public static FileStreamRtn doPostDownFile(PostMethod method) {
        FileStreamRtn streamRtn=new FileStreamRtn();
        System.out.println("------------");
        ByteArrayInputStream fileStream=null;
        for (NameValuePair parameter : method.getParameters()) {
            System.out.println(parameter.getName()+":"+parameter.getValue());
        }
        HttpClient client = new HttpClient();
        client.getHttpConnectionManager().getParams().setConnectionTimeout(60000);
        client.getHttpConnectionManager().getParams().setSoTimeout(60000);
        client.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, "UTF-8");
        Map result=new HashMap();
        try {

            client.executeMethod(method);
            Header[] headers=method.getResponseHeaders();
            Header header=method.getResponseHeader("Content-Disposition");
            fileStream=new ByteArrayInputStream(method.getResponseBody());
            streamRtn.setHeaders(headers);
            streamRtn.setFileStream(fileStream);
            if(header!=null){
                streamRtn.setContentDisposition(header.getValue());
            }
        } catch (Exception e) {
            throw new RuntimeException("创建连接失败" + e);
        } finally {
            method.releaseConnection();
        }
        System.out.println(result);
        return streamRtn;
    }

    // post请求
    public static String doPost(PostMethod method) {
        System.out.println("------------");
        for (NameValuePair parameter : method.getParameters()) {
            System.out.println(parameter.getName()+":"+parameter.getValue());
        }
        //忽略https证书检验
        try {
            SslUtils.ignoreSsl();
        }catch (Exception e){
        }
        HttpClient client = new HttpClient();
        client.getHttpConnectionManager().getParams().setConnectionTimeout(60000);
        client.getHttpConnectionManager().getParams().setSoTimeout(60000);
        client.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, "UTF-8");
        String result = "";
        try {

            client.executeMethod(method);
            byte[] response = method.getResponseBody();
            result = new String(response, "UTF-8");//返回值解析时用的编码格式
        } catch (Exception e) {
            throw new RuntimeException("创建连接失败" + e);
        } finally {
            method.releaseConnection();
        }
        System.out.println(result);
        if(StringUtils.isNotEmpty(result)){

        }
        return result;
    }
    // get请求
    public static String doGet(GetMethod method) {
        System.out.println("------------");
        HttpClient client = new HttpClient();
        client.getHttpConnectionManager().getParams().setConnectionTimeout(60000);
        client.getHttpConnectionManager().getParams().setSoTimeout(60000);
        client.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, "UTF-8");
        String result = "";
        try {

            client.executeMethod(method);
            byte[] response = method.getResponseBody();
            result = new String(response, "UTF-8");//返回值解析时用的编码格式
        } catch (Exception e) {
            throw new RuntimeException("创建连接失败" + e);
        } finally {
            method.releaseConnection();
        }
        System.out.println(result);
        return result;
    }
    // 获取接口sign
    public static String getUrlSign(String timestamp, String token, String url) {
        String result = "";
        Map<String, String> m = new LinkedHashMap<>();
        m.put("timestamp", timestamp);
        m.put("token", token);
        m.put("url", url);
        try {
            result = getSign(m);
        } catch (Exception e) {
            return "";
        }
        return result;
    }

    // 发送获取token的get请求
    public static String tokenGet(String url, String key, String time, String sign) {

        String param = "key=" + key + "&timestamp=" + time + "&sign=" + sign;
        String result = "";
        BufferedReader in = null;
        try {
            String urlNameString = url + "?" + param;
            URL realUrl = new URL(urlNameString);
            // 打开和URL之间的连接
            URLConnection connection = realUrl.openConnection();
            // 设置通用的请求属性
            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("user-agent",
                    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            // 建立实际的连接
            connection.connect();
            // 定义 BufferedReader输入流来读取URL的响应
            in = new BufferedReader(new InputStreamReader(
                    connection.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            System.out.println("发送GET请求出现异常！" + e);
            e.printStackTrace();
        }
        // 使用finally块来关闭输入流
        finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
        System.out.println(result);
        Map<String, Object> map = new HashMap<>();
        map = gson.fromJson(result, map.getClass());
        map = gson.fromJson(obTstr(map.get("data")), map.getClass());
        String token = obTstr(map.get("token"));
        System.out.println("====token====");
        System.out.println(token);
        System.out.println("=============");
        return token;
    }

    // 获取token的sign
    public static String getTokenSign(String timestamp, String key, String secret) {
        String result = "";
        Map<String, String> m = new LinkedHashMap<>();
        m.put("key", key);
        m.put("secret", secret);
        m.put("timestamp", timestamp);
        try {
            result = getSign(m);
        } catch (Exception e) {
            result = "";
        }
        return result;
    }

    // 生成sign
    public static String getSign(Map<String, String> map) {
        String result = "";
        try {
            List<Map.Entry<String, String>> infoIds = new ArrayList<>(map.entrySet());
            // 对所有传入参数按照字段名的 ASCII 码从小到大排序（字典序）
            Collections.sort(infoIds, new Comparator<Map.Entry<String, String>>() {
                public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {
                    return (o1.getKey()).compareTo(o2.getKey());
                }
            });
            // 构造签名键值对的格式
            StringBuilder sb = new StringBuilder();
            for (Map.Entry<String, String> item : infoIds) {
                if (item.getKey() != null || item.getKey() != "") {
                    String key = item.getKey();
                    String val = item.getValue();
                    if (!(val == "" || val == null)) {
                        sb.append(key + "=" + val + "&");
                    }
                }
            }
            if (sb.length() > 1) {
                result = sb.substring(0, sb.length() - 1);
            }
            System.out.println("拼接生成sign->" + result);
            //进行MD5加密
            result = DigestUtils.md5Hex(result).toUpperCase();
        } catch (Exception e) {
            return null;
        }
        return result;
    }

    // 获取时间戳
    public static String getTime() {
        return obTstr(System.currentTimeMillis());
    }

    // 转字符串
    public static String obTstr(Object ob) {
        return ob == null ? "" : ob.toString().trim();
    }


}
